#include "pj_taskExec.hpp"
#include "json/json.h"

/**************************************  STATIC VARIALBES *******************************/
//send data back
static const int maxSendDataLength = 409600;

//prepare
static const vector<string> validLanguages = {"cpp", "js"};

//build
static const string buildOutputInfoPos = "buildout.txt";
static const string buildErrorInfoPos = "builderror.txt";
static const string buildOutPos = "main.out";
static const int BuildFileWaitInterval_us = 200000;
static const int BuildFileMaxWaitTimes = 50;

static const int buildSonProcessErr_execlp = 1;
static const int buildSonProcessOK = 0;

static const int buildSonProcessErr_dupout = 2;
static const int buildSonProcessErr_duperr = 3;

static const int RunFileWaitInterval_us = 200000;
static const int RunFileMaxWaitTimes = 100;

//run

/***** pre define the return map, to make sure that: if in my cpp file, when trying to set one key:value where key not occur, just make some exception happen!*****/
// note: run test and run submit uses same predefinied maps

// internal error or build error
static const unordered_map<string, string> PJ_INTERNAL_ERROR = {
	{"code", "0"},
	{"info", "you should overlap it"}
};

static const unordered_map<string, string> PJ_BUILD_ERROR = {
	{"code", "1"},
	{"info", "you should overlap it"}
};

// run code error or ok
static const unordered_map<string, string> PJ_BUILD_AC = {
	{"code", "2"},
	{"time", "99999"},
	{"memory", "9999999999"},
	{"input", ""},
	{"realans", ""},
	{"rightans", ""},
	{"errorinfo", ""},
	{"outputinfo", ""}
};

static const unordered_map<string, string> PJ_BUILD_WA = {
	{"code", "3"},
	{"time", "99999"},
	{"memory", "9999999999"},
	{"input", ""},
	{"realans", ""},
	{"rightans", ""},
	{"errorinfo", ""},
	{"outputinfo", ""}
};

static const unordered_map<string, string> PJ_BUILD_TLE = {
	{"code", "4"},
	{"time", "99999"},
	{"memory", "9999999999"},
	{"input", ""},
	{"realans", ""},
	{"rightans", ""},
	{"errorinfo", ""},
	{"outputinfo", ""}
};

static const unordered_map<string, string> PJ_BUILD_MLE = {
	{"code", "5"},
	{"time", "99999"},
	{"memory", "9999999999"},
	{"input", ""},
	{"realans", ""},
	{"rightans", ""},
	{"errorinfo", ""},
	{"outputinfo", ""}
};

static const unordered_map<string, string> PJ_BUILD_RE = {
	{"code", "6"},
	{"time", "99999"},
	{"memory", "9999999999"},
	{"input", ""},
	{"realans", ""},
	{"rightans", ""},
	{"errorinfo", ""},
	{"outputinfo", ""}
};

static const unordered_map<string, string> PJ_BUILD_OF = {
	{"code", "7"},
	{"time", "99999"},
	{"memory", "9999999999"},
	{"input", ""},
	{"realans", ""},
	{"rightans", ""},
	{"errorinfo", ""},
	{"outputinfo", ""}
};

/*
 * parse raw data, JSON string->JSON:Value
 * para: const string& raw, Json::Value& root
 * return pair<bool, Json::Value>: code true with none, false with error string
 *
 * note: set value of root inside the function
 */
pair<bool, string> parseRawData(const string& raw, Json::Value& root){
	JSONCPP_STRING errs;
	Json::CharReaderBuilder readerBuilder;
	std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
	bool res;
	res = jsonReader->parse(raw.c_str(), raw.c_str() + raw.length(), &root, &errs);
	if (!res || !errs.empty()){
		return {false, (string)errs};
	}
	else return {true, ""};
}

/*
 * generate res data, parse means: unordered_map -> JSON:Value
 * para: const unordered_map<string, string>& um
 * return string(JSON:Value)
 */
string genResData(const unordered_map<string, string>& um)
{
	Json::Value v;
	for (const auto& iterum:um) v[iterum.first] = iterum.second;
	Json::StreamWriterBuilder sw;
	sw.settings_["indentation"] = "";
	std::ostringstream osStream;
	string resStr;

	Json::StreamWriter* psw = sw.newStreamWriter();
	psw->write(v, &osStream);
	string ret = osStream.str();
	if (ret.size() > maxSendDataLength){
		//regenerate um
		unordered_map<string, string> newum = PJ_INTERNAL_ERROR;
		newum.at("info") = (string)"cpp pj system internal error: send back data too long";
		return genResData(newum);
	}
	else{
		return osStream.str();
	}
}

/*
 * execute shell build command
 * para: string languageStr, in previous version, only "cpp" is valid!
 *       string fullDiretory: should be "xxx/yyy/zz"
		 string submitIDStr: is a string, should be kkk
 *       bool isSubmit: true if need build with suffix:"-D submit"
 * return: pair<bool, unordered_map<string, string>>, only if build ok can return {true, {}},
 *     otherwise return {false, specific unordered_map for TCP sending back}
 */
pair<bool, unordered_map<string, string>> buildFile(string languageStr, 
	string fullDiretory, string submitIDStr, bool isSubmit){
	//generate filename, public
    string buildOutPutInfoFilename = fullDiretory + "/" + submitIDStr + buildOutputInfoPos;
    string buildErrorInfoFilename = fullDiretory + "/" + submitIDStr + buildErrorInfoPos;
    string buildOutFilename = fullDiretory + "/" + submitIDStr + buildOutPos;
    string buildCppFile = fullDiretory + "/" + submitIDStr + usercodeFileNameMid + cppPos;
	
	//fork
    int pid = fork();
    if (pid == -1){
		unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
		ret.at("info") = (string)"cpp pj system internal error: buildFile: fork error";
		return {false, ret};
    }
    else if (pid == 0){ //child
		//execlp prepare
		//cout << "worker: buildFile: child process: before execlp" << endl;
		//char buf[1024] = {0};
		//getcwd(buf, sizeof(buf));
		//cout << "pre work dir:" << buf << endl;
		//cout << "g++ --std=c++11 " << buildCppFile << " -I" << " mjson/" << " -I" << " /home/blacksea3/vcpkg/installed/x64-linux/include/" << " -o " << buildOutFilename << " -Lmjson/" << " -lmjson" << " -L/home/blacksea3/vcpkg/installed/x64-linux/debug/lib/" << " -ljsoncpp" << " -fsanitize=address" << " -fno-omit-frame-pointer" << " -D" << " submit " << buildOutPutInfoBPara << " " << buildErrorInfoBPara << endl;
		
        //redirect stdout and stderr
        //do not need to save oldout/olderr for re redirect because the process will exit(1) after excelp
        //int oldout = dup(STDOUT_FILENO);
        //int olderr = dup(STDERR_FILENO);
        int dupOutput, dupError;
        dupOutput = dupError = -1;
        dupOutput = open(buildOutPutInfoFilename.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644);
        if (dupOutput == -1) exit(buildSonProcessErr_dupout);
        if (dup2(dupOutput, STDOUT_FILENO) == -1) exit(buildSonProcessErr_dupout);
        dupError = open(buildErrorInfoFilename.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644);
        if (dupError == -1) exit(buildSonProcessErr_duperr);
        if (dup2(dupError, STDERR_FILENO) == -1) exit(buildSonProcessErr_duperr);
		
		//execlp
		if (isSubmit){
			if (execlp("g++", "g++", "--std=c++11", buildCppFile.c_str(), "-I", "mjson/", "-I", "/home/blacksea3/vcpkg/installed/x64-linux/include/", "-o", buildOutFilename.c_str(), "-Lmjson/", "-lmjson", "-L/home/blacksea3/vcpkg/installed/x64-linux/debug/lib/", "-ljsoncpp", "-fsanitize=address", "-fno-omit-frame-pointer", "-D", "submit", NULL) == -1){
					exit(buildSonProcessErr_execlp);  //error
				}
			else{  //no error outside execlp
				exit(buildSonProcessOK);
			}
		}
		else{
			if (execlp("g++", "g++", "--std=c++11", buildCppFile.c_str(), "-I", "mjson/", "-I", "/home/blacksea3/vcpkg/installed/x64-linux/include/", "-o", buildOutFilename.c_str(), "-Lmjson/", "-lmjson", "-L/home/blacksea3/vcpkg/installed/x64-linux/debug/lib/", "-ljsoncpp", "-fsanitize=address", "-fno-omit-frame-pointer", NULL) == -1){
					exit(buildSonProcessErr_execlp);  //error
				}
			else{  //no error outside execlp
				exit(buildSonProcessOK);
			}
		}
    }
    else{  //parent
		//wait for build end
        for (int i = 0; i < BuildFileMaxWaitTimes; ++i){
            usleep(BuildFileWaitInterval_us);
            int status;
            int ret = waitpid(pid, &status, WNOHANG);
            if (ret == -1){
				unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
				ret.at("info") = (string)"cpp pj system internal error: buildFile: waitpid error";
				return {false, ret};
            }
            else if (ret != 0){
                if (WIFEXITED(status)){  //exit normally
					pair<bool, string> outputInfoReadRet = ReadFileToString(buildOutPutInfoFilename);
					pair<bool, string> errorInfoReadRet = ReadFileToString(buildErrorInfoFilename);
					
					cout << "worker: buildFile: parent process: child exited" << endl;
					
					int delOutputInfo = remove(buildOutPutInfoFilename.c_str());
					int delErrorInfo = remove(buildErrorInfoFilename.c_str());
					
					if (!outputInfoReadRet.first || !errorInfoReadRet.first){
						unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
						ret.at("info") = (string)"cpp pj system internal error: buildFile: cannot read buildout.txt and/or builderror.txt";
						return {false, ret};
					}
					else if (delOutputInfo == -1 || delErrorInfo == -1){
						unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
						ret.at("info") = (string)"cpp pj system internal error: buildFile: cannot delete buildout.txt and/or builderror.txt";
						return {false, ret};
					}
					else{
						//judge build status by check if out file exists
						if (access(buildOutFilename.c_str(), F_OK) != 0){   //build error(file not exist)
							unordered_map<string, string> ret = PJ_BUILD_ERROR;
							string fullErrorContent = outputInfoReadRet.second + "/" + errorInfoReadRet.second;
							ret.at("info") = fullErrorContent;
							return {false, ret};
						}
						else{  //build ok
							return {true, {}};
						}
					}
                }
                else if (WIFSIGNALED(status)){
					unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
					ret.at("info") = (string)("cpp pj system internal error: buildFile: child process killey by" + to_string(WEXITSTATUS(status)));
					return {false, ret};
                }
                else{
					unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
					ret.at("info") = (string)("cpp pj system internal error: buildFile: child process exit with unknown reason");
					return {false, ret};
                }
            }
            else{
                continue;
            }
        }
		//build time limit exceed
        if (kill(pid, SIGKILL) == -1){  //note here: assume that killing child is very quick, I don't use waitpid to wait for child exit!
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)("cpp pj system internal error: buildFile time limit exceed, parent kill child error");
			return {false, ret};
        }
        else{
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)("cpp pj system internal error: buildFile time limit exceed, parent kill child success");
			return {false, ret};
        }
    }
}

/*
 * execute run file
 * para: string languageStr(), in previous version, support "cpp" or "js"
 *    string fullDiretory: should be "xxx/yyy/zzz"
 *    string submitIDStr: shou be kkk
 *    int maxTimeSecond
 *    int maxMemoryMB
 *    bool isSubmit
 * return: unordered_map<string, string>, result!
 */
unordered_map<string, string> runFile(string languageStr, string fullDiretory, 
	string submitIDStr, int maxtimeInt, int maxmemoryInt, bool isSubmit){
	//generate public filename/directory
	string fullDiretoryWithSlice = fullDiretory + "/";
	
	//fork
    int pid = fork();
    if (pid == -1){
		unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
		ret.at("info") = (string)"cpp pj system internal error: runFile: fork error";
		return ret;
    }
    else if (pid == 0){ //child
		//generate filename/directory name
		string maxtimeStr = to_string(maxtimeInt);
		string maxmemoryStr = to_string(maxmemoryInt);
		string isSubmitStr = (isSubmit) ? "1" : "0";
		string languageConStr = (languageStr == "cpp") ? "0" : "1";
		
		// ../cppJudgeSystem/judge 99999 v7test/sort/cpp/ 1 20 0 0 (最后的0 0 表示c++语言和跑单个testcase)
		//../cppJudgeSystem/judge 1 v7test/sort/js/ 1 20 1 1 (最后的1 1 表示javascript语言和跑多组testcases)
		if (execlp("cppJudgeSystem/judge", "cppJudgeSystem/judge",
			submitIDStr.c_str(), fullDiretoryWithSlice.c_str(), maxtimeStr.c_str(),
			maxmemoryStr.c_str(), languageConStr.c_str(), isSubmitStr.c_str(), NULL) == -1){
				exit(GBL_JSBP_EXECLP_ERROR);
			}
		else{ //dump, because execlp will return(exit) inside!
			exit(GBL_JSBP_EXECLP_DUMP);
		}
    }
    else{  //parent
        for (int countTime = 0; countTime < RunFileMaxWaitTimes; ++countTime){
            usleep(RunFileWaitInterval_us);
            int status;
            int ret = waitpid(pid, &status, WNOHANG);
            if (ret == -1){
				unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
				ret.at("info") = (string)("cpp pj system internal error: runFile: waitpid error");
				return ret;
            }
            else if (ret != 0){
                if (WIFEXITED(status)){
					//generate filename, public
					string runErrorInfoFilename  = fullDiretoryWithSlice + submitIDStr + RunErrorInfoPos;
					string runTimeFilename       = fullDiretoryWithSlice + submitIDStr + RunTimePos;
					string runMemoryFilename     = fullDiretoryWithSlice + submitIDStr + RunMemoryPos;
					string runInputFilename      = fullDiretoryWithSlice + submitIDStr + RunInputPos;
					string runRealansFilename    = fullDiretoryWithSlice + submitIDStr + RunRealansPos;
					string runRightansFilename   = fullDiretoryWithSlice + submitIDStr + RunRightansPos;
					string runOutPutInfoFilename = fullDiretoryWithSlice + submitIDStr + RunOutputInfoPos;
					
					//check the group of childRetCode
                    int childRetCode = WEXITSTATUS(status);
					int groupRetCode = childRetCode/16;					
					if (groupRetCode == 2){
						if (childRetCode == GBL_JSBP_RES_DEBUG || childRetCode == GBL_JSBP_RES_ERROR ||
							childRetCode == GBL_JSBP_RES_UNKNOWN){
							unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
							ret.at("info") = (string)("cpp pj system internal error: runFile: JSBP level: ");
							if (childRetCode == GBL_JSBP_RES_DEBUG){
								ret.at("info") += "GBL_JSBP_RES_DEBUG";
							}
							else if (childRetCode == GBL_JSBP_RES_ERROR){
								pair<bool, string> ErrorInfoReadRet  = ReadFileToString(runErrorInfoFilename);
								if (ErrorInfoReadRet.first){
									ret.at("info") = ErrorInfoReadRet.second;
								}
								else{
									ret.at("info") = "GBL_JSBP_RES_ERROR: cannot open errorinfo.txt";
								}
							}
							else{
								ret.at("info") += "GBL_JSBP_RES_UNKNOWN";
							}
							return ret;
						}
						else{
							//select one type return code!
							unordered_map<string, string> ret;
							switch(childRetCode){
								case GBL_JSBP_RES_TLE:{
									ret = PJ_BUILD_TLE;
									break;
								}
								case GBL_JSBP_RES_OF:{
									ret = PJ_BUILD_OF;
									break;
								}
								case GBL_JSBP_RES_MLE:{
									ret = PJ_BUILD_MLE;
									break;
								}
								case GBL_JSBP_RES_RE:{
									ret = PJ_BUILD_RE;
									break;
								}
								case GBL_JSBP_RES_WA:{
									ret = PJ_BUILD_WA;
									break;
								}
								case GBL_JSBP_RES_AC:{
									ret = PJ_BUILD_AC;
									break;
								}
								default:{
									unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
									ret.at("info") = (string)("cpp pj system internal error: runFile: taskExec get an unknown return code:" + to_string(childRetCode));
									return ret;
								}
							}
							
							//get so many infos!!!!!!!!!!
							pair<bool, string> ErrorInfoReadRet  = ReadFileToString(runErrorInfoFilename);
							pair<bool, string> TimeReadRet       = ReadFileToString(runTimeFilename);
							pair<bool, string> MemoryReadRet     = ReadFileToString(runMemoryFilename);
							pair<bool, string> InputReadRet      = ReadFileToString(runInputFilename);
							pair<bool, string> RealansReadRet    = ReadFileToString(runRealansFilename);
							pair<bool, string> RightansReadRet   = ReadFileToString(runRightansFilename);
							pair<bool, string> OutPutInfoReadRet = ReadFileToString(runOutPutInfoFilename);
							
							//generate return string, neglect these cannot open file, if open, "neglect if delete failed!"
							if (ErrorInfoReadRet.first){
								ret.at("errorinfo") = ErrorInfoReadRet.second;
								remove(runErrorInfoFilename.c_str()); //don't check its return code
							}
							if (TimeReadRet.first){
								ret.at("time") = TimeReadRet.second;
								remove(runTimeFilename.c_str()); //don't check its return code
							}
							if (MemoryReadRet.first){
								ret.at("memory") = MemoryReadRet.second;
								remove(runMemoryFilename.c_str()); //don't check its return code
							}
							if (InputReadRet.first){
								ret.at("input") = InputReadRet.second;
								remove(runInputFilename.c_str()); //don't check its return code
							}
							if (RealansReadRet.first){
								ret.at("realans") = RealansReadRet.second;
								remove(runRealansFilename.c_str()); //don't check its return code
							}
							if (RightansReadRet.first){
								ret.at("rightans") = RightansReadRet.second;
								remove(runRightansFilename.c_str()); //don't check its return code
							}
							if (OutPutInfoReadRet.first){
								ret.at("outputinfo") = OutPutInfoReadRet.second;
								remove(runOutPutInfoFilename.c_str()); //don't check its return code
							}
							return ret;
						}
					}
					else if (groupRetCode == 3){
						unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
						switch(childRetCode){
							case GBL_JSBP_OUTER_ERROR_PARA:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_OUTER_ERROR_PARA";
								break;
							}
							case GBL_JSBP_OUTER_ERROR_FORK:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_OUTER_ERROR_FORK";
								break;
							}
							case GBL_JSBP_OUTER_ERROR_WAIT_CHILD:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_OUTER_ERROR_WAIT_CHILD";
								break;
							}
							case GBL_JSBP_OUTER_ERROR_UNKNOWN_CHILD:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_OUTER_ERROR_UNKNOWN_CHILD";
								break;
							}
							case GBL_JSBP_OUTER_ERROR_SAVE_INFO:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_OUTER_ERROR_SAVE_INFO";
								break;
							}
							case GBL_JSBP_OUTER_ERROR_READ_OUTPUT:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_OUTER_ERROR_READ_OUTPUT";
								break;
							}
							case GBL_JSBP_OUTER_ERROR_DEBUG:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_OUTER_ERROR_DEBUG";
								break;
							}
							case GBL_JSBP_EXECLP_ERROR:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_EXECLP_ERROR";
								break;
							}
							case GBL_JSBP_EXECLP_DUMP:{
								ret.at("info") = "cpp pj system internal error: JSBP level: GBL_JSBP_EXECLP_DUMP";
								break;
							}
							default:{
								ret.at("info") = (string)("cpp pj system internal error: runFile: taskExec get an unknown return code:" + to_string(childRetCode));
								break;
							}
						}
						return ret;
					}
					else{
						unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
						ret.at("info") = (string)("cpp pj system internal error: runFile: taskExec get an unknown return code:" + to_string(childRetCode));
						return ret;
					}
                }
                else if (WIFSIGNALED(status)){
					unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
					ret.at("info") = (string)("cpp pj system internal error: runFile: child process killey by" + to_string(WEXITSTATUS(status)));
					return ret;
                }
                else{
					unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
					ret.at("info") = (string)("cpp pj system internal error: runFile: child process exit with unknown reason");
					return ret;
                }
            }
            else{
                continue;
            }
        }
        //run file real time limit exceed
        if (kill(pid, SIGKILL) == -1){  //note here: assume that killing child is very quick, I don't use waitpid to wait for child exit!
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)("cpp pj system internal error: runFile: real time limit exceed, parent kill child error");
			return ret;
        }
        else{
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)("cpp pj system internal error: runFile: real time limit exceed, parent kill child success");
			return ret;
        }
    }
	
	return {};
}

/*
 * entry function of task, exec the target task
 * para: string recvRawStr, JSON type
 * return: string, (returnStr, JSON type)
 */
string taskExec(const string& recvRawStr){
	//parse data(json string->jSON::Value)
	Json::Value val;
	pair<bool, string> parseRawRet = parseRawData(recvRawStr, val);
	if (!parseRawRet.first){
		unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
		ret.at("info") = (string)"cpp pj system internal error: code invalid: " + parseRawRet.second;
		return genResData(ret);
	}
	
	//check val type and code
	if (!val.isObject() || !val.isMember("code")){
		unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
		ret.at("info") = (string)"cpp pj system internal error: code invalid: " + "val type and/or code error";
		return genResData(ret);
	}
	
	if (val["code"] == "10" || val["code"] == "11"){    //run custom testcase or run submit
		bool isCustomTestcase = (val["code"] == "10");
	
		//check all the info values type
		if (!val.isMember("dir") || !val["dir"].isString() || !val.isMember("language") || !val["language"].isString() ||
			!val.isMember("usercode") || !val["usercode"].isString() ||
			!val.isMember("maxtime") || !val["maxtime"].isString() || !val.isMember("maxmemory") || !val["maxmemory"].isString() ||
			!val.isMember("submitID") || !val["submitID"].isString()
			){
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)"cpp pj system internal error: require fields missing or type error";
			return genResData(ret);
		}
		//extra check of custom testcase
		if (isCustomTestcase && (!val.isMember("input") || !val["input"].isString())){
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)"cpp pj system internal error: require fields missing or type error";
			return genResData(ret);
		}
		
		//convert JSON::value to string
		string languageStr = val["language"].asString();
		string dirStr = val["dir"].asString();
		string usercodeStr = val["usercode"].asString();
		string maxtimeStr = val["maxtime"].asString();
		string maxmemoryStr = val["maxmemory"].asString();
		string submitIDStr = val["submitID"].asString();
		
		//extra string of custom testcase
		string inputStr = "?????";
		if (isCustomTestcase) inputStr = val["input"].asString();
		
		//check language/dir/submitID
		if (find(validLanguages.begin(), validLanguages.end(), languageStr) == validLanguages.end()){
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)"cpp pj system internal error: language not found";
			return genResData(ret);
		}
		regex dirRe("[a-zA-Z][a-zA-Z0-9_]{3,20}/[a-zA-Z][a-zA-Z0-9_]{3,50}");
		if (!regex_match(dirStr, dirRe)){
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)"cpp pj system internal error: dir format invalid";
			return genResData(ret);
		}
		
		//check maxtime/maxmemory
		int maxtimeInt;
		int maxmemoryInt;
		try {
			maxtimeInt = stoi(maxtimeStr);
			maxmemoryInt = stoi(maxmemoryStr);
			if (maxtimeInt > 10 || maxtimeInt < 1 || maxmemoryInt > 200 || maxmemoryInt < 20){
				unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
				ret.at("info") = (string)"cpp pj system internal error: maxtime/maxmemory out of range";
				return genResData(ret);
			}
		}
		catch (std::invalid_argument&){
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)"cpp pj system internal error: maxtime/maxmemory format invalid";
			return genResData(ret);
		}
		catch (std::out_of_range&){
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)"cpp pj system internal error: maxtime/maxmemory out of range";
			return genResData(ret);
		}
		catch (...) {
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)"cpp pj system internal error: maxtime/maxmemory unknown error";
			return genResData(ret);
		}
		
		//save usercode
		string fullUsercodeFilename = dirStr + "/" + languageStr + "/" + submitIDStr + usercodeFileNameMid;
		if (languageStr == "cpp") fullUsercodeFilename += cppPos;
		else fullUsercodeFilename += jsPos;
		pair<bool, string> usercodeSaveRet = WriteStringTofile(fullUsercodeFilename, usercodeStr);
		if (!usercodeSaveRet.first){
			unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
			ret.at("info") = (string)("cpp pj system internal error: save usercode, " + usercodeSaveRet.second);
			return genResData(ret);
		}
		//save custominput of custom testcase
		if (isCustomTestcase){
			string fullInputFileName = dirStr + "/" + languageStr + "/" + submitIDStr + UserInputPos;
			pair<bool, string> inputSaveRet = WriteStringTofile(fullInputFileName, inputStr);
			if (!inputSaveRet.first){
				unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
				ret.at("info") = (string)"cpp pj system internal error: save input error";
				return genResData(ret);
			}
		}
		
		//build
		string fullDiretory = dirStr + "/" + languageStr;
		if (languageStr == "cpp"){
			pair<bool, unordered_map<string, string>> buildScriptRes = buildFile(languageStr, fullDiretory, submitIDStr, !isCustomTestcase); //note: the last bool of buildFile is isSubmit
			if (!buildScriptRes.first)  //buildTestFile error occured
			{
				return genResData(buildScriptRes.second);
			}
		}
		
		//run
		return genResData(runFile(languageStr, fullDiretory, submitIDStr, maxtimeInt, maxmemoryInt, !isCustomTestcase)); //note: the last bool of runFile is isSubmit
	}
	else{
		unordered_map<string, string> ret = PJ_INTERNAL_ERROR;
		ret.at("info") = (string)"cpp pj system error: code invalid: " + "code not found";
		return genResData(ret);
	}
}
